package matching;

import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;


import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.DocumentHelper;

public class supervisor 
{
	ArrayList<String> connectDB;
	private int listenPort;
	private int multicastToMatchMakerPort;
	private String multicastIP;
	private boolean debug;
	private mysqlmethods sql;

	public supervisor(ArrayList<String> c, String _multicastIP, int _mulitcastToMatchMakerPort, int _listenPort, 
			boolean _debug)
	{
		connectDB = c;
		listenPort = _listenPort;
		multicastToMatchMakerPort = _mulitcastToMatchMakerPort;
		multicastIP = _multicastIP;
		debug = _debug;
	}

	public void listen()
	{
		try {
			ServerSocket listener = new ServerSocket(listenPort);


			while(true)
			{
				System.out.print("Listening for connections on port " + listenPort + " from MatchMaker.");
				Socket client = listener.accept();
				Thread t = new Thread(new SupervisorHandler(client, connectDB));
				t.start();
				System.out.println("Connected - "+client.getInetAddress());
			}
		} catch (IOException e) {
			System.out.println("Issue listening for connections");
			e.printStackTrace();
		}
	}




	class SupervisorHandler implements Runnable
	{
		final String togive_element;
		final String totake_element;
		final String howlong_element;
		final String isoffer_element;
		final String id_element;
		final String contact_element;
		final String match_element;


		BufferedReader is;
		private final Socket socket;

		private boolean debug;

		private String xml;
		private ArrayList<match> matches;

		private ArrayList<String> sqlConnect;
		

		public SupervisorHandler(Socket incomingSocket, ArrayList<String> _sqlConnect) throws IOException
		{

			sqlConnect = _sqlConnect;
			sql = new mysqlmethods(sqlConnect.get(0), sqlConnect.get(1), sqlConnect.get(2), sqlConnect.get(3));

			xml = null;
			socket = incomingSocket;
			is = new BufferedReader(new InputStreamReader(
					new BufferedInputStream(socket.getInputStream())));

			matches = new ArrayList<match>();

			match_element = "match";
			togive_element = "togive";
			totake_element = "totake";
			howlong_element = "howlong";
			isoffer_element = "isoffer";
			id_element = "id";
			contact_element = "contact";


		}

		public void run(){
			try{
				String line = null;
				while((line = is.readLine()) != null)
				{

					System.out.println(line);
					xml += line;
				}

				socket.close();
				is.close();

				//This fills up matches array
				this.readXML(xml);

				for(int i = 0; i < matches.size(); i++)
				{
					match m = matches.get(i);
					if(this.matchStillExists(m))
					{
						Thread t = new Thread(new SupervisorSender(multicastIP ,multicastToMatchMakerPort, m));
						//this will remove the ids from the database as well as contact the matchmaker
						//To remove them as well from its data structures.
						t.start();
						this.contactItems(m);
					}
				}




			}//try
			catch(Exception e)
			{
				System.err.println("1ST OUCH! " + e);
			}
			finally
			{
				try
				{
					System.out.println("Should be closing socket and reader" + "\n");
					socket.close();
					is.close();
				}
				catch(Exception ee)
				{
					System.err.println("2ND  OUCH! "+ee);
				}
			}//finally
		}//run

		//this method picks the best match from the matches array
		//right now this method will just pick 


		private boolean matchStillExists(match m)
		{
			boolean exist = false;
			//1st need to check all matches ids are still in the database...
			for(int i =0; i < m.getMatchSize(); i++)
			{
				item it = m.getItem(i);
				String var = sql.getValue(it.getId() + "", "id", "rides");
				if(var.equalsIgnoreCase( it.getId() + ""))
				{
					exist = true;
				}
				else
				{
					exist = false;
					break;
				}


			}

			return exist;
		}

		private void contactItems(match _m)
		{
			match m = _m;
			ArrayList<String> c = new ArrayList<String>();

			//this gets all of the contacts for match m
			for(int x = 0; x < m.getMatchSize(); x++)
			{
				String contact = m.getItem(x).getContact();
				c.add(contact);
			}



			for(int i = 0; i < c.size(); i++)
			{
				if(debug)
					System.out.println("Inside contactItem");

				if(c.get(i).matches(".*@.*") == true)
				{
					//This is an email address
					if(debug)
						System.out.println("We found an email address");

				}
				else
				{
					if(c.get(i) == "")
						return;
					
					if(debug)
						System.out.println("We found a bot trying to contact...." + c.get(i));

					String con[] = c.get(i).split(":");
					String host = con[0];
					if(debug)
						System.out.println("The issue is parsing " + con[1]);
					int port = Integer.parseInt(con[1]);

					PrintWriter out = null;

					if(debug)
						System.out.println("After splitting and stuff");

					Socket MyClient = null;
					
					System.out.println("Sending found match signal to " + host + ":" + port);
					
					try {
						MyClient = new Socket(host, port);
						out = new PrintWriter(MyClient.getOutputStream(),true);
						for(int j =0; j < c.size(); j++)
						{
							//This makes sure we dont tell a part they are matched with themself
							if(i!=j)
								out.println("You have been matched with " + m.getItem(j).toString());
						}

						if (out != null)
							out.close();
						if (MyClient != null)
							try {
								MyClient.close();
							} catch (IOException e) {
								
								e.printStackTrace();
							}

					}
					catch (IOException e) {
						System.out.println(e);
					}


				}
			}



		}//method

		public void readXML(String xmlT)
		{
			xmlT = xmlT.replaceFirst("null", "");
			if(debug)
				System.out.println("PARSING: " + xmlT);
			try { 

				Document document = DocumentHelper.parseText(xmlT);
				this.fromXML(document);
			} catch (DocumentException e) {
				throw new RuntimeException("Error reading file xml. In Read XML. " + e.getMessage());
			}
		}

		//this method transforms back from an XML match
		//to a matches array
		private void fromXML(Document document)
		{
			matches.clear();

			Element root = document.getRootElement();
			for (Object elementObj : root.elements(match_element)) 
			{
				if(debug)
					System.out.println("IN FOR IN fromXML");
				Element element = (Element) elementObj;
				ArrayList<item> items = new ArrayList<item>();
				String matchsize = getElementStringAttribute(element,"matchsize");
				for(int i = 0; i < Integer.parseInt(matchsize); i++)
				{
					if(i == 0)
					{
						String togive = getElementStringAttribute(element,togive_element);
						String totake = getElementStringAttribute(element,totake_element);
						String howlong = getElementStringAttribute(element,howlong_element);
						String isoffer = getElementStringAttribute(element,isoffer_element);
						String id = getElementStringAttribute(element,id_element);
						String contact = getElementStringAttribute(element,contact_element);
						item it = new item(Integer.parseInt(id), togive, totake, Integer.parseInt(howlong), isoffer, contact);
						items.add(it);

					}
					else
					{
						int i2 = i+1;
						String togive = getElementStringAttribute(element,togive_element+i2);
						String totake = getElementStringAttribute(element,totake_element+i2);
						String howlong = getElementStringAttribute(element,howlong_element+i2);
						String isoffer = getElementStringAttribute(element,isoffer_element+i2);
						String id = getElementStringAttribute(element,id_element+i2);
						String contact = getElementStringAttribute(element,contact_element+i2);
						item it = new item(Integer.parseInt(id), togive, totake, Integer.parseInt(howlong), isoffer, contact);
						items.add(it);

					}

				}




				match m =  new match(items);
				if(debug)
					System.out.println("Adding Match: " + m);
				matches.add(m);

			}
			if(debug)
				System.out.println("Matches Size = " + matches.size());

		}
		//helper method for fromXML
		private String getElementStringAttribute(
				Element element, String attributeName) {

			Attribute attribute = getNonNullAttributeForElement(element, attributeName);
			return attribute.getValue();
		}
		//helper method for fromXML
		private Attribute getNonNullAttributeForElement(
				Element element, String attributeName) {

			Attribute attribute = element.attribute(attributeName);
			if (attribute == null) {
				throw new RuntimeException("Element " + element.getName() +
						" missing attribute named " + attributeName + ".");
			}
			return attribute;
		}	 
	}//class
	class SupervisorSender implements Runnable
	{
		private String multicastGroup;
		private int portMatchMaker;
		private match m;
		private InetAddress group;
		
		
		public SupervisorSender(String _multicastGroup, int _portMatchMaker, match _m)
		{
			multicastGroup = _multicastGroup;
			portMatchMaker = _portMatchMaker;
			m = _m;
		}
		//This run method will call remove match(m)
		public void run() {
			
			this.removeMatch(m);
		}
		public void removeMatch(match m)
		{
			System.out.println("Sending remove match call to matchmakers");
			//calls the SQL to remove
			for(int i = 0; i < m.getMatchSize(); i++)
				sql.removeOffer("rides", m.getItem(i).getId());
			
			if(debug)
				System.out.println("IN REMOVE MATCH..");
			try {
				DatagramSocket socket = new DatagramSocket();
				byte[] b = new byte[1024];
				String toSend = "remove";

				for(int i = 0; i < m.getMatchSize(); i++)
				{
					toSend = toSend + " " + m.getItem(i).getId();
				}
				if(debug)
					System.out.println("Sending:" + toSend);
				b = toSend.getBytes();
				group = InetAddress.getByName(multicastGroup);
				
				if(debug)
					System.out.println("group = " + group);
				
				DatagramPacket packet = new DatagramPacket(b,
						b.length,
						group, portMatchMaker);
				socket.send(packet);
			
			} 
			catch (Exception e) {
				e.printStackTrace();
				System.out.println("Something went wrong!");
			}
		}

	}//SupervisorSender
}//class
